package it.unicam.cs.cosy.bioshape.core.behaviours;

import it.unicam.cs.cosy.bioshape.core.components.Pair;

import java.util.ArrayList;

/**
 * represents a strong split operator of the Shape Calculus. Differently from
 * the weak split, this operator can release <i>more</i> than one bond at a
 * time. It can be used, at an atomic level of modelling, to represents the
 * occurrence of chemical reactions.
 * 
 * @author Federico Buti
 */
public class StrongSplit extends Behaviour {

	/** Auto-generated Serial ID */
	private static final long serialVersionUID = 6154572696064048831L;
	private ArrayList<Pair<String, String>> channels;

	/**
	 * @return the channels
	 */
	public ArrayList<Pair<String, String>> getChannels() {
		return channels;
	}

	/**
	 * @return the channels
	 */
	public int getChannelsCard() {
		return channels.size();
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = super.hashCode();
		result = prime * result
				+ ((channels == null) ? 0 : channels.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (!super.equals(obj))
			return false;
		if (getClass() != obj.getClass())
			return false;
		StrongSplit other = (StrongSplit) obj;
		if (channels == null) {
			if (other.channels != null)
				return false;
		} else if (!channels.equals(other.channels))
			return false;
		return true;
	}

	/**
	 * @param channels
	 *            List of bonds to be released
	 * @param component
	 *            Next {@code Behaviour} after the split is executed.
	 */
	public StrongSplit(ArrayList<Pair<String, String>> channels,
			Behaviour component) {
		this.channels = new ArrayList<Pair<String, String>>();
		this.channels.addAll(channels);
		super.component = component;
		label = "\u03F1" + "(" + stringChannels(channels) + ")."
				+ component.getLabel();
	}

	private String stringChannels(ArrayList<Pair<String, String>> chann) {
		StringBuilder b = new StringBuilder();
		for (int i = 0; i < chann.size() - 1; i++)
			b.append("\u3008" + chann.get(i).getE1() + ", "
					+ chann.get(i).getE2() + "\u3009, ");
		b.append("\u3008" + chann.get(chann.size() - 1).getE1() + ", "
				+ chann.get(chann.size() - 1).getE2() + "\u3009");
		return b.toString();
	}

	/**
	 * Checks if this {@code StrongSplit} is abstractly compatible with the
	 * passed split. Two {@code StrongSplit} objects are abstractly compatible
	 * if they split two compatible channel names (name - coname) regardless of
	 * the shape features on which they are defined.<br>
	 * The compatibility concept is <i>relaxed</i>: this split is compatible
	 * with the passed one if at least <i>one</i> couple of bonds to break are
	 * compatible. <i>External</i> code must ensure that the not compatible
	 * channels are checked against other {@code StrongSplit} objects<br>
	 * WARNING: the method does NOT consider {@code Choice} operators. Instead
	 * the user must iterate over the {@code Choice} operator behaviours and
	 * check each of them against the considered {@code StrongSplit}.
	 * 
	 * @param behaviour
	 *            The {@code Behaviour} to check against.
	 * 
	 * @return {@code true} if the two processes are compatible splits,
	 *         {@code false} otherwise
	 */
	public boolean isAbstractCompatible(Behaviour behaviour) {
		if (behaviour.getClass() != StrongSplit.class)
			return false;
		//
		return checkCompatibility((StrongSplit) behaviour, false);
	}

	/**
	 * Checks if this {@code WeakSplit} is compatible with the passed split. Two
	 * {@code WeakSplit} objects are compatible if they split two compatible
	 * channel names (name - coname) and also the set of features is the same.<br>
	 * The compatibility concept is <i>relaxed</i>: this split is compatible
	 * with the passed one if at least <i>one</i> couple of bonds to break are
	 * compatible. <i>External</i> code must ensure that the not compatible
	 * channels are checked against other {@code StrongSplit} objects<br>
	 * WARNING: the method does NOT consider {@code Choice} operators. Instead
	 * the user must iterated over the {@code Choice} operator behaviours and
	 * check each of them against the considered {@code StrongSplit}.
	 * 
	 * @param behaviour
	 *            The {@code Behaviour} to check against.
	 * 
	 * @return {@code true} if the two processes are compatible splits,
	 *         {@code false} otherwise
	 */
	public boolean isCompatible(Behaviour behaviour) {
		if (behaviour.getClass() != StrongSplit.class)
			return false;
		//
		return checkCompatibility((StrongSplit) behaviour, true);
	}

	/**
	 * Executes the real checking for compatibility. It can alternatively check
	 * that two strong splits are compatible abstractly or concretely.<br>
	 * The compatibility concept is <i>relaxed</i>: this split is compatible
	 * with the passed one if at least <i>one</i> couple of bonds to break are
	 * compatible. <i>External</i> code must ensure that the not compatible
	 * channels are checked against other {@code StrongSplit} objects<br>
	 * 
	 * @param behaviour
	 *            {@code StrongSplit} to check against for compatibility.
	 * @param b
	 *            Flag to choose for abstract compatibility of concrete.
	 *            {@code true} to check concrete compatibility, {@code false}
	 *            otherwise.
	 * 
	 * @return {@code true} if the two strong splits are compatible,
	 *         {@code false} otherwise
	 * 
	 * @see #isCompatible(Behaviour)
	 * @see #isAbstractCompatible(Behaviour)
	 */
	private boolean checkCompatibility(StrongSplit behaviour, boolean b) {
		// TODO
		// if (action.indexOf('\'') != -1 && ss.action.indexOf('\'') != -1)
		// return false;
		// if (action.indexOf('\'') == 0 && ss.action.indexOf('\'') == 0)
		// return false;
		// String act1, act2;
		// act1 = action.indexOf('\'') != -1 ? action.substring(1) : action;
		// act2 = ws.action.indexOf('\'') != -1 ? ws.action.substring(1)
		// : ws.action;
		// return act1.equals(act2) && site.getName().equals(ws.site.getName());
		// act1.equals(act2) && site.getName().equals(ws.site.getName());
		return false;
	}
}
